/***
 * 网络模块
 */
define(['globals', 'struct', 'md5'], function ($, struct, md5) {

  var CONF = {
    Status: 'R228',
    // Debug: 'ws://192.168.0.88:19200/19200', // 18275|18670|8270
    // Realse: 'ws://47.99.171.33:18971',
    // R228: 'ws://47.111.30.228:18060',
    // R105: 'ws://192.168.0.105:8645'
    // R105: 'ws://192.168.0.105:18645/18645'
    // R228: 'ws://47.111.30.228:18643/18643'
  };

  var WSocket = WebSocket || window.MozWebSocket;
  if (!WSocket) {
    console.log("浏览器不支持WebSocket!");
  }
  var net = {};
  // https://www.w3.org/TR/websockets/#websocket
  // readyState
  // const unsigned short CONNECTING = 0;
  // const unsigned short OPEN = 1;
  // const unsigned short CLOSING = 2;
  // const unsigned short CLOSED = 3;
  var _ws = null;
  var _RconCount = 0; // 断线重连幂数 1*2^0 ~ 1*2^5
  var _SendQueue = []; // 发送队列
  var _RecvBuff = new Uint8Array(0);
  var _IsVerify = 0;

  function initConnect() {
    var url = CONF[CONF['Status']];
    console.log('initConnect', url);
    if (!_ws || _ws.readyState != WSocket.OPEN) {
      _ws = new WSocket(url);
      _ws.binaryType = "arraybuffer";
      _ws.onopen = OnOpen;
      _ws.onclose = OnClose;
      _ws.onmessage = OnMessage;
    }
  }

  function OnOpen(e) {
    console.log('连接成功');
    _RconCount = 0;
  }

  function OnClose(e) { // e.code=1005客户端手动断开; 1006=服务器端不可连接; 1000=服务器主动断开;
    console.log("ws.OnClose", e.code);
    _IsVerify = 0;
    if (_RconCount > 4) {
      _RconCount = 0;
    }
    var t = Math.pow(2, _RconCount);
    _RconCount++;
    console.log("连接被断开, " + t + "秒后重连..");
    setTimeout(initConnect, t * 1000);
  }

  function OnMessage(e /* MessageEvent */) {
    console.log("收到数据[" + e.data.byteLength + "]", e.data);
    //------ 追加开始 ------
    var tmpBuf = new Uint8Array(_RecvBuff.byteLength + e.data.byteLength);  // 预先分配长度
        tmpBuf.set(_RecvBuff, 0);  // 将`_RecvBuff`拷贝至tmpBuf的0偏移处
        tmpBuf.set(new Uint8Array(e.data), _RecvBuff.byteLength); // 追加新数据
    _RecvBuff = tmpBuf;
    //------ 追加结束 ------
    while (_RecvBuff.byteLength >= 12) { // 同时拆解多个消息包, 包头固定12字节
/*
BYTE    cbDataKind;   //数据类型
BYTE    cbCheckCode;  //效验字段
WORD    wPacketSize;  //数据总大小(包头+包体)
DWORD   dwMessageID;  //消息ID
WORD    wMainCmdID;   //主命令码
WORD    wSubCmdID;    //子命令码
*/
      var pbf = new DataView(_RecvBuff.buffer);
      var DataKind    = pbf.getUint8(0);
      var CheckCode   = pbf.getUint8(1);
      var PacketSize  = pbf.getUint16(2, true);
      var MessageID   = pbf.getUint32(4, true);


      if (PacketSize < 12 || PacketSize > 16384) {
        throw new Error("数据长度不符合, 当前" + PacketSize);
      }
      if (PacketSize > _RecvBuff.byteLength) {
        throw new Error("消息长度不符合, 总长=" + _RecvBuff.byteLength + ", 包长=" + PacketSize);
      }

      tmpBuf = _RecvBuff.slice(0, PacketSize); // 截取数据包
      _RecvBuff = _RecvBuff.slice(PacketSize); // 消耗`_RecvBuff`

      // 字节码映射
      if (DataKind != 0) {
        var CheckCode = CheckCode;
        for (var i = 8; i < tmpBuf.byteLength; i++) { // 从第9字节开始
          var code = tmpBuf[i];
              code = s_RecvByteMap[code];
          CheckCode += code;
          CheckCode %= 256;
          tmpBuf[i] = code;
        }
        if (CheckCode != 0) {
          throw new Error('数据映射失败');
        }
      }

      pbf = new DataView(tmpBuf.buffer);
      var MainCmdID   = pbf.getUint16(8, true);
      var SubCmdID    = pbf.getUint16(10, true);
      tmpBuf = tmpBuf.slice(12);  // 包体
      console.log('收到消息[' + MainCmdID + '_' + SubCmdID + '][' + tmpBuf.byteLength + ']');

      if (tmpBuf.byteLength > 0) {  // 包体有数据
        var call2 = "Unpack_" + MainCmdID + "_" + SubCmdID;
        if (typeof(struct[call2]) != 'function') {
          console.warn(call2 + " is not defined");
          continue;
        }
        var body = struct[call2](tmpBuf);
      }

      if (typeof(body) == "undefined") {
        body = {};
      }

      var call3 = "MSG_" + MainCmdID + "_" + SubCmdID;
      console.log("广播消息[" + call3 + "]", body);
      $.publish(call3, {data:body});
    }
  }
  
  $.subscribe('MSG_0_2', function (e, data) { // 内部验证
    var UUID = '511C9B89D558D7BAAD20B6090902302B';
    var TransType = data.data.TransType;
    var TimeStamp = data.data.TimeStamp + '';
    var Strindex = TimeStamp[TransType];
    var changestr = (((parseInt(Strindex) + 48) * TransType) + 10) + '';
    var str1 = TimeStamp.substring(0, TransType);
    var str2 = TimeStamp.substring(TransType, TimeStamp.length);
    var str3 = str1 + changestr + str2 + UUID;
    var sign = md5(str3);
    var buf = struct.Pack_33_2019(UUID, 0, sign);
    net.send(0, 2, buf.buffer);
    _IsVerify = 1;
    net.send(19, 3); // 请求开服列表
  });
  
  $.subscribe('MSG_0_1', function (e, data) { // 心跳
    net.send(0, 1);
    while (_SendQueue.length > 0) {
      var msg = _SendQueue.shift(); // 数组为空返回undefined
      if (msg) {
        console.log('发送[' + msg.mid + '][' + msg.sid + '][' + msg.buf.byteLength + '字节]');
        _ws.send(msg.buf);
      }
    }
  });
  
  $.subscribe('MSG_19_50', function (e, data) { // 获取开服列表完成
    console.log("获取开服列表完成");
    net.send(19, 5); // 请求在线用户列表
  });
  
  $.subscribe('MSG_19_90', function (e, data) { // 获取在线用户列表完成
    console.log('获取在线用户列表完成');
  });
  
  // _ws.send(data), data为`DOMString/Blob/ArrayBuffer/ArrayBufferView`中任意类型
  net.send = function (wMainCmd, wSubCmd, data /* ArrayBuffer */) {
    if (!WSocket) {
      console.warn('浏览器不支持WebSocket!');
      return;
    }

    var ecx = 12 + (data ? data.byteLength : 0);
    var buf = new Uint8Array(ecx);
    var pbf = new DataView(buf.buffer);

    pbf.setUint8(0, 1);
    pbf.setUint8(1, 0);
    pbf.setUint16(2, ecx, true);
    pbf.setUint32(4, 0);
    pbf.setUint16(8, wMainCmd, true);
    pbf.setUint16(10, wSubCmd, true);
/*
BYTE    cbDataKind;   //数据类型
BYTE    cbCheckCode;  //效验字段
WORD    wPacketSize;  //数据总大小(包头+包体)
DWORD   dwMessageID;  //消息ID
WORD    wMainCmdID;   //主命令码
WORD    wSubCmdID;    //子命令码
*/
    if (ecx > 12) {
      buf.set(new Uint8Array(data), 12);
    }

    //---------- 字节码映射 -----------
    var cbCheckCode = 0;
    for (var i = 8; i < buf.byteLength; i++) {
      var pos = pbf.getUint8(i);
      cbCheckCode += pos;
      cbCheckCode = cbCheckCode % 256;
      var val = s_SendByteMap[pos];
      pbf.setUint8(i, val);
    }
    pbf.setUint8(1, ~cbCheckCode + 1);
    //-----------------------------------
    if (!_ws || _ws.readyState != WSocket.OPEN) { // 未连接
      _SendQueue.push({mid: wMainCmd, sid: wSubCmd, buf: buf.buffer});
      initConnect();
    } else { // 已连接
      if (wMainCmd == 0 && wSubCmd == 2) {
        console.log('发送[' + wMainCmd + '][' + wSubCmd + '][' + buf.byteLength + '字节]');
        _ws.send(buf.buffer);
      } else {
        if (_IsVerify) {
          console.log('发送[' + wMainCmd + '][' + wSubCmd + '][' + buf.byteLength + '字节]');
          _ws.send(buf.buffer);
        } else {
          _SendQueue.push({mid: wMainCmd, sid: wSubCmd, buf: buf.buffer});
        }
      }
    }

  };

  var s_RecvByteMap = [
    0x51, 0xA1, 0x9E, 0xB0, 0x1E, 0x83, 0x1C, 0x2D, 0xE9, 0x77, 0x3D, 0x13, 0x93, 0x10, 0x45, 0xFF,
    0x6D, 0xC9, 0x20, 0x2F, 0x1B, 0x82, 0x1A, 0x7D, 0xF5, 0xCF, 0x52, 0xA8, 0xD2, 0xA4, 0xB4, 0x0B,
    0x31, 0x97, 0x57, 0x19, 0x34, 0xDF, 0x5B, 0x41, 0x58, 0x49, 0xAA, 0x5F, 0x0A, 0xEF, 0x88, 0x01,
    0xDC, 0x95, 0xD4, 0xAF, 0x7B, 0xE3, 0x11, 0x8E, 0x9D, 0x16, 0x61, 0x8C, 0x84, 0x3C, 0x1F, 0x5A,
    0x02, 0x4F, 0x39, 0xFE, 0x04, 0x07, 0x5C, 0x8B, 0xEE, 0x66, 0x33, 0xC4, 0xC8, 0x59, 0xB5, 0x5D,
    0xC2, 0x6C, 0xF6, 0x4D, 0xFB, 0xAE, 0x4A, 0x4B, 0xF3, 0x35, 0x2C, 0xCA, 0x21, 0x78, 0x3B, 0x03,
    0xFD, 0x24, 0xBD, 0x25, 0x37, 0x29, 0xAC, 0x4E, 0xF9, 0x92, 0x3A, 0x32, 0x4C, 0xDA, 0x06, 0x5E,
    0x00, 0x94, 0x60, 0xEC, 0x17, 0x98, 0xD7, 0x3E, 0xCB, 0x6A, 0xA9, 0xD9, 0x9C, 0xBB, 0x08, 0x8F,
    0x40, 0xA0, 0x6F, 0x55, 0x67, 0x87, 0x54, 0x80, 0xB2, 0x36, 0x47, 0x22, 0x44, 0x63, 0x05, 0x6B,
    0xF0, 0x0F, 0xC7, 0x90, 0xC5, 0x65, 0xE2, 0x64, 0xFA, 0xD5, 0xDB, 0x12, 0x7A, 0x0E, 0xD8, 0x7E,
    0x99, 0xD1, 0xE8, 0xD6, 0x86, 0x27, 0xBF, 0xC1, 0x6E, 0xDE, 0x9A, 0x09, 0x0D, 0xAB, 0xE1, 0x91,
    0x56, 0xCD, 0xB3, 0x76, 0x0C, 0xC3, 0xD3, 0x9F, 0x42, 0xB6, 0x9B, 0xE5, 0x23, 0xA7, 0xAD, 0x18,
    0xC6, 0xF4, 0xB8, 0xBE, 0x15, 0x43, 0x70, 0xE0, 0xE7, 0xBC, 0xF1, 0xBA, 0xA5, 0xA6, 0x53, 0x75,
    0xE4, 0xEB, 0xE6, 0x85, 0x14, 0x48, 0xDD, 0x38, 0x2A, 0xCC, 0x7F, 0xB1, 0xC0, 0x71, 0x96, 0xF8,
    0x3F, 0x28, 0xF2, 0x69, 0x74, 0x68, 0xB7, 0xA3, 0x50, 0xD0, 0x79, 0x1D, 0xFC, 0xCE, 0x8A, 0x8D,
    0x2E, 0x62, 0x30, 0xEA, 0xED, 0x2B, 0x26, 0xB9, 0x81, 0x7C, 0x46, 0x89, 0x73, 0xA2, 0xF7, 0x72,
  ];

  var s_SendByteMap = [
    0x70, 0x2F, 0x40, 0x5F, 0x44, 0x8E, 0x6E, 0x45, 0x7E, 0xAB, 0x2C, 0x1F, 0xB4, 0xAC, 0x9D, 0x91,
    0x0D, 0x36, 0x9B, 0x0B, 0xD4, 0xC4, 0x39, 0x74, 0xBF, 0x23, 0x16, 0x14, 0x06, 0xEB, 0x04, 0x3E,
    0x12, 0x5C, 0x8B, 0xBC, 0x61, 0x63, 0xF6, 0xA5, 0xE1, 0x65, 0xD8, 0xF5, 0x5A, 0x07, 0xF0, 0x13,
    0xF2, 0x20, 0x6B, 0x4A, 0x24, 0x59, 0x89, 0x64, 0xD7, 0x42, 0x6A, 0x5E, 0x3D, 0x0A, 0x77, 0xE0,
    0x80, 0x27, 0xB8, 0xC5, 0x8C, 0x0E, 0xFA, 0x8A, 0xD5, 0x29, 0x56, 0x57, 0x6C, 0x53, 0x67, 0x41,
    0xE8, 0x00, 0x1A, 0xCE, 0x86, 0x83, 0xB0, 0x22, 0x28, 0x4D, 0x3F, 0x26, 0x46, 0x4F, 0x6F, 0x2B,
    0x72, 0x3A, 0xF1, 0x8D, 0x97, 0x95, 0x49, 0x84, 0xE5, 0xE3, 0x79, 0x8F, 0x51, 0x10, 0xA8, 0x82,
    0xC6, 0xDD, 0xFF, 0xFC, 0xE4, 0xCF, 0xB3, 0x09, 0x5D, 0xEA, 0x9C, 0x34, 0xF9, 0x17, 0x9F, 0xDA,
    0x87, 0xF8, 0x15, 0x05, 0x3C, 0xD3, 0xA4, 0x85, 0x2E, 0xFB, 0xEE, 0x47, 0x3B, 0xEF, 0x37, 0x7F,
    0x93, 0xAF, 0x69, 0x0C, 0x71, 0x31, 0xDE, 0x21, 0x75, 0xA0, 0xAA, 0xBA, 0x7C, 0x38, 0x02, 0xB7,
    0x81, 0x01, 0xFD, 0xE7, 0x1D, 0xCC, 0xCD, 0xBD, 0x1B, 0x7A, 0x2A, 0xAD, 0x66, 0xBE, 0x55, 0x33,
    0x03, 0xDB, 0x88, 0xB2, 0x1E, 0x4E, 0xB9, 0xE6, 0xC2, 0xF7, 0xCB, 0x7D, 0xC9, 0x62, 0xC3, 0xA6,
    0xDC, 0xA7, 0x50, 0xB5, 0x4B, 0x94, 0xC0, 0x92, 0x4C, 0x11, 0x5B, 0x78, 0xD9, 0xB1, 0xED, 0x19,
    0xE9, 0xA1, 0x1C, 0xB6, 0x32, 0x99, 0xA3, 0x76, 0x9E, 0x7B, 0x6D, 0x9A, 0x30, 0xD6, 0xA9, 0x25,
    0xC7, 0xAE, 0x96, 0x35, 0xD0, 0xBB, 0xD2, 0xC8, 0xA2, 0x08, 0xF3, 0xD1, 0x73, 0xF4, 0x48, 0x2D,
    0x90, 0xCA, 0xE2, 0x58, 0xC1, 0x18, 0x52, 0xFE, 0xDF, 0x68, 0x98, 0x54, 0xEC, 0x60, 0x43, 0x0F,
  ];

  return $.extend({ws: net});
});
